#!/usr/bin/env python3
"""
清理 MinIO 中的孤立文件
每天运行一次，删除数据库中没有引用的文件
"""
import sys
import os
from pathlib import Path

# 添加项目根目录到 Python 路径
project_root = Path(__file__).parent.parent
sys.path.insert(0, str(project_root))

from app.core.database import SessionLocal
from app.models.user import User
from app.models.team import Team, TeamMember
from app.services.minio_service import MinIOService
from app.core.config import settings
from datetime import datetime
from loguru import logger

def get_all_referenced_files():
    """获取数据库中所有引用的文件"""
    db = SessionLocal()
    referenced_files = set()
    
    try:
        # 1. 用户头像
        users = db.query(User).all()
        for user in users:
            if user.avatar:
                # 提取文件路径: https://minio.aqilmini.com/football/avatars/xxx.jpg -> avatars/xxx.jpg
                if '/football/' in user.avatar:
                    file_path = user.avatar.split('/football/')[-1]
                    referenced_files.add(file_path)
        
        # 2. 球队Logo
        teams = db.query(Team).all()
        for team in teams:
            if team.logo:
                if '/football/' in team.logo:
                    file_path = team.logo.split('/football/')[-1]
                    referenced_files.add(file_path)
        
        # 3. 球队成员头像
        members = db.query(TeamMember).all()
        for member in members:
            if member.avatar:
                if '/football/' in member.avatar:
                    file_path = member.avatar.split('/football/')[-1]
                    referenced_files.add(file_path)
        
        return referenced_files
    
    finally:
        db.close()

def get_all_minio_files():
    """获取 MinIO 中的所有文件"""
    minio_service = MinIOService()
    all_files = set()
    
    try:
        # 列出所有文件
        objects = minio_service.client.list_objects(
            settings.MINIO_BUCKET,
            recursive=True
        )
        
        for obj in objects:
            all_files.add(obj.object_name)
        
        return all_files
    
    except Exception as e:
        logger.error(f"获取 MinIO 文件列表失败: {e}")
        return set()

def clean_orphaned_files(dry_run=True):
    """清理孤立文件"""
    logger.info("=" * 60)
    logger.info(f"🧹 开始清理孤立文件 (dry_run={dry_run})")
    logger.info(f"⏰ 时间: {datetime.now().isoformat()}")
    logger.info("=" * 60)
    
    # 获取数据库中引用的文件
    logger.info("\n📊 扫描数据库引用...")
    referenced_files = get_all_referenced_files()
    logger.info(f"✅ 数据库中引用了 {len(referenced_files)} 个文件")
    
    # 获取 MinIO 中的所有文件
    logger.info("\n📦 扫描 MinIO 存储...")
    minio_files = get_all_minio_files()
    logger.info(f"✅ MinIO 中有 {len(minio_files)} 个文件")
    
    # 找出孤立文件
    orphaned_files = minio_files - referenced_files
    
    if not orphaned_files:
        logger.success("\n🎉 没有发现孤立文件，存储空间整洁！")
        return
    
    logger.warning(f"\n⚠️  发现 {len(orphaned_files)} 个孤立文件:")
    
    # 按文件夹分类统计
    stats = {}
    for file_path in orphaned_files:
        folder = file_path.split('/')[0]
        stats[folder] = stats.get(folder, 0) + 1
    
    logger.info("\n📈 按文件夹统计:")
    for folder, count in stats.items():
        logger.info(f"  - {folder}/: {count} 个文件")
    
    # 列出所有孤立文件
    logger.info("\n📝 孤立文件列表:")
    for file_path in sorted(orphaned_files):
        logger.info(f"  - {file_path}")
    
    # 删除文件
    if dry_run:
        logger.warning("\n⚠️  DRY RUN 模式 - 不会实际删除文件")
        logger.info("运行 `python3 scripts/clean_orphaned_files.py --delete` 来实际删除")
    else:
        logger.warning("\n🗑️  开始删除孤立文件...")
        minio_service = MinIOService()
        deleted_count = 0
        failed_count = 0
        
        for file_path in orphaned_files:
            try:
                minio_service.client.remove_object(
                    settings.MINIO_BUCKET,
                    file_path
                )
                logger.success(f"  ✅ 已删除: {file_path}")
                deleted_count += 1
            except Exception as e:
                logger.error(f"  ❌ 删除失败: {file_path} - {e}")
                failed_count += 1
        
        logger.info("\n" + "=" * 60)
        logger.success(f"✅ 删除完成: {deleted_count} 个")
        if failed_count > 0:
            logger.error(f"❌ 删除失败: {failed_count} 个")
        logger.info("=" * 60)

def clean_invalid_db_references():
    """清理数据库中的无效文件引用"""
    logger.info("\n🔍 检查数据库中的无效引用...")
    
    db = SessionLocal()
    minio_files = get_all_minio_files()
    
    fixed_count = 0
    
    try:
        # 检查用户头像
        users = db.query(User).all()
        for user in users:
            if user.avatar and '/football/' in user.avatar:
                file_path = user.avatar.split('/football/')[-1]
                if file_path not in minio_files:
                    logger.warning(f"  清空用户 {user.nick_name} 的无效头像: {user.avatar}")
                    user.avatar = None
                    fixed_count += 1
        
        # 检查球队Logo
        teams = db.query(Team).all()
        for team in teams:
            if team.logo and '/football/' in team.logo:
                file_path = team.logo.split('/football/')[-1]
                if file_path not in minio_files:
                    logger.warning(f"  清空球队 {team.name} 的无效Logo: {team.logo}")
                    team.logo = None
                    fixed_count += 1
        
        # 检查球队成员头像
        members = db.query(TeamMember).all()
        for member in members:
            if member.avatar and '/football/' in member.avatar:
                file_path = member.avatar.split('/football/')[-1]
                if file_path not in minio_files:
                    logger.warning(f"  清空成员 {member.user_name} 的无效头像: {member.avatar}")
                    member.avatar = None
                    fixed_count += 1
        
        db.commit()
        
        if fixed_count > 0:
            logger.success(f"✅ 已清空 {fixed_count} 个无效的文件引用")
        else:
            logger.success("✅ 数据库中没有无效引用")
    
    finally:
        db.close()

if __name__ == "__main__":
    import argparse
    
    parser = argparse.ArgumentParser(description="清理 MinIO 中的孤立文件")
    parser.add_argument(
        "--delete",
        action="store_true",
        help="实际删除文件（默认只是预览）"
    )
    parser.add_argument(
        "--clean-db",
        action="store_true",
        help="同时清理数据库中的无效引用"
    )
    
    args = parser.parse_args()
    
    try:
        # 清理孤立文件
        clean_orphaned_files(dry_run=not args.delete)
        
        # 清理数据库引用
        if args.clean_db:
            clean_invalid_db_references()
        
        logger.success("\n✨ 清理任务完成！")
    
    except Exception as e:
        logger.error(f"\n❌ 清理失败: {e}")
        import traceback
        traceback.print_exc()
        sys.exit(1)

